PIC Tutorial Two - Switches


For the first parts of this tutorial you require the Main Board and the Switch Board, the later parts will also use the LED Board, as written the tutorials use the Switch Board on PortA and the LED Board on PortB. Download zipped tutorial files.

Tutorial 2.1 - requires Main Board and Switch Board.

This simple program turns the corresponding LED on, when the button opposite it is pressed, extinguishing all other LED's.

;Tutorial 2.1 - Nigel Goodwin 2002
	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)


LEDPORT	Equ	PORTA			;set constant LEDPORT = 'PORTA'
SWPORT	Equ	PORTA			;set constant SWPORT = 'PORTA'
LEDTRIS	Equ	TRISA			;set constant for TRIS register
SW1	Equ	7			;set constants for the switches
SW2	Equ	6
SW3	Equ	5
SW4	Equ	4
LED1	Equ	3			;and for the LED's
LED2	Equ	2
LED3	Equ	1
LED4	Equ	0

;end of defines
	
	org	0x0000			;org sets the origin, 0x0000 for the 16F628,
					;this is where the program starts running	
	movlw	0x07
	movwf	CMCON			;turn comparators off (make it like a 16F84)

   	bsf 	STATUS,		RP0	;select bank 1
   	movlw 	b'11110000'		;set PortA 4 inputs, 4 outputs
   	movwf 	LEDTRIS
	bcf	STATUS,		RP0	;select bank 0
	clrf	LEDPORT			;set all outputs low


Loop	btfss	SWPORT,	SW1
	call	Switch1
	btfss	SWPORT,	SW2
	call	Switch2
	btfss	SWPORT,	SW3
	call	Switch3
	btfss	SWPORT,	SW4
	call	Switch4
	goto	Loop

Switch1	clrf	LEDPORT			;turn all LED's off
	bsf	SWPORT,	LED1		;turn LED1 on
	retlw	0x00

Switch2	clrf	LEDPORT			;turn all LED's off
	bsf	SWPORT,	LED2		;turn LED2 on
	retlw	0x00

Switch3	clrf	LEDPORT			;turn all LED's off
	bsf	SWPORT,	LED3		;turn LED3 on
	retlw	0x00

Switch4	clrf	LEDPORT			;turn all LED's off
	bsf	SWPORT,	LED4		;turn LED4 on
	retlw	0x00


	end      
      

As with the previous tutorials we first set things up, then the main program runs in a loop, the first thing the loop does is check switch SW1 with the 'btfss SWPORT, SW1' line, if the switch isn't pressed the input line is held high by the 10K pull-up resistor and it skips the next line. This takes it to the 'btfss SWPORT, SW2' line, where SW2 is similarly checked - this continues down checking all the switches and then loops back and checks them again. If a key is pressed, the relevant 'btfss' doesn't skip the next line, but instead calls a sub-routine to process the key press, each switch has it's own sub-routine. These sub-routines are very simple, they first 'clrf' the output port, turning all LED's off, and then use 'bsf' to turn on the corresponding LED, next the sub-routine exits via the 'retlw' instruction. As the switch is likely to be still held down, the same routine will be run again (and again, and again!) until you release the key, however for this simple application that isn't a problem and you can't even tell it's happening.

Tutorial 2.2 - requires Main Board and Switch Board.

This program toggles the corresponding LED on and off, when the button opposite it is pressed. It introduces the concept of 'de-bouncing' - a switch doesn't close immediately, it 'bounces' a little before it settles, this causes a series of fast keypresses which can cause chaos in operating a device.

;Tutorial 2.2 - Nigel Goodwin 2002
	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)

	cblock 	0x20 			;start of general purpose registers
		count1 			;used in delay routine
		counta 			;used in delay routine 
		countb 			;used in delay routine
	endc

LEDPORT	Equ	PORTA			;set constant LEDPORT = 'PORTA'
SWPORT	Equ	PORTA			;set constant SWPORT = 'PORTA'
LEDTRIS	Equ	TRISA			;set constant for TRIS register
SW1	Equ	7			;set constants for the switches
SW2	Equ	6
SW3	Equ	5
SW4	Equ	4
LED1	Equ	3			;and for the LED's
LED2	Equ	2
LED3	Equ	1
LED4	Equ	0

SWDel	Set	Del50			;set the de-bounce delay (has to use 'Set' and not 'Equ')

;end of defines
	
	org	0x0000			;org sets the origin, 0x0000 for the 16F628,
					;this is where the program starts running	
	movlw	0x07
	movwf	CMCON			;turn comparators off (make it like a 16F84)

   	bsf 	STATUS,		RP0	;select bank 1
   	movlw 	b'11110000'		;set PortA 4 inputs, 4 outputs
   	movwf 	LEDTRIS
	bcf	STATUS,		RP0	;select bank 0
	clrf	LEDPORT			;set all outputs low


Loop	btfss	SWPORT,	SW1
	call	Switch1
	btfss	SWPORT,	SW2
	call	Switch2
	btfss	SWPORT,	SW3
	call	Switch3
	btfss	SWPORT,	SW4
	call	Switch4
	goto	Loop

Switch1	call	SWDel			;give switch time to stop bouncing
	btfsc	SWPORT,	SW1		;check it's still pressed
	retlw	0x00			;return is not
	btfss	SWPORT,	LED1		;see if LED1 is already lit
	goto	LED1ON
	goto	LED1OFF

LED1ON	bsf	LEDPORT,	LED1	;turn LED1 on
	call	SWDel
	btfsc	SWPORT,	SW1		;wait until button is released
	retlw	0x00
	goto	LED1ON	

LED1OFF	bcf	LEDPORT,	LED1	;turn LED1 on
	call	SWDel
	btfsc	SWPORT,	SW1		;wait until button is released
	retlw	0x00
	goto	LED1OFF		

Switch2	call	SWDel			;give switch time to stop bouncing
	btfsc	SWPORT,	SW2		;check it's still pressed
	retlw	0x00			;return is not
	btfss	SWPORT,	LED2		;see if LED2 is already lit
	goto	LED2ON
	goto	LED2OFF

LED2ON	bsf	LEDPORT,	LED2	;turn LED2 on
	call	SWDel
	btfsc	SWPORT,	SW2		;wait until button is released
	retlw	0x00
	goto	LED2ON	

LED2OFF	bcf	LEDPORT,	LED2	;turn LED2 on
	call	SWDel
	btfsc	SWPORT,	SW2		;wait until button is released
	retlw	0x00
	goto	LED2OFF

Switch3	call	SWDel			;give switch time to stop bouncing
	btfsc	SWPORT,	SW3		;check it's still pressed
	retlw	0x00			;return is not
	btfss	SWPORT,	LED3		;see if LED3 is already lit
	goto	LED3ON
	goto	LED3OFF

LED3ON	bsf	LEDPORT,	LED3	;turn LED3 on
	call	SWDel
	btfsc	SWPORT,	SW3		;wait until button is released
	retlw	0x00
	goto	LED3ON	

LED3OFF	bcf	LEDPORT,	LED3	;turn LED3 on
	call	SWDel
	btfsc	SWPORT,	SW3		;wait until button is released
	retlw	0x00
	goto	LED3OFF

Switch4	call	SWDel			;give switch time to stop bouncing
	btfsc	SWPORT,	SW4		;check it's still pressed
	retlw	0x00			;return is not
	btfss	SWPORT,	LED4		;see if LED4 is already lit
	goto	LED4ON
	goto	LED4OFF

LED4ON	bsf	LEDPORT,	LED4	;turn LED4 on
	call	SWDel
	btfsc	SWPORT,	SW4		;wait until button is released
	retlw	0x00
	goto	LED4ON	

LED4OFF	bcf	LEDPORT,	LED4	;turn LED4 on
	call	SWDel
	btfsc	SWPORT,	SW4		;wait until button is released
	retlw	0x00
	goto	LED4OFF

;modified Delay routine, direct calls for specified times
;or load W and call Delay for a custom time.

Del0	retlw	0x00			;delay 0mS - return immediately
Del1	movlw	d'1'			;delay 1mS
	goto	Delay
Del5	movlw	d'5'			;delay 5mS
	goto	Delay
Del10	movlw	d'10'			;delay 10mS
	goto	Delay
Del20	movlw	d'20'			;delay 20mS
	goto	Delay
Del50	movlw	d'50'			;delay 50mS
	goto	Delay
Del100	movlw	d'100'			;delay 100mS
	goto	Delay
Del250	movlw	d'250'			;delay 250 ms
Delay	movwf	count1
d1	movlw	0xC7			;delay 1mS
	movwf	counta
	movlw	0x01
	movwf	countb
Delay_0
	decfsz	counta, f
	goto	$+2
	decfsz	countb, f
	goto	Delay_0

	decfsz	count1	,f
	goto	d1
	retlw	0x00
	end      
      

In order to de-bounce the keypresses we delay for a short time, then check that the button is still pressed, the time delay is set by the variable SWDel, which is defined as Del50 in the defines section at the start of the program. I've extended the Delay routine to provide a selection of different delays (from 0mS to 250mS), called by simple 'call' instructions, the Delay routine itself can also be called directly - simply load the required delay into the W register and 'call Delay'. We then check to see if the corresponding LED is lit, with 'btfss SWPORT, LEDx', and jump to either 'LEDxON' or 'LEDxOFF', these routines are almost identical, the only difference being that the first turns the LED on, and the second turns it off. They first switch the LED, on or off, depending on which routine it is, and then delay again (calling SWDel as before), next they check to see if the button is still pressed, looping back around if it is. Once the key has been released the routine exits via the usual 'retlw' and returns to waiting for a keypress. I've used the variable SWDel (and provided the various delay times) so that you can easily try the effect of different delay times - in particular try setting SWDel to Del0, and see how the button pressing isn't reliable, you will probably find one of the buttons is worse than the others - particularly if you use old switches, wear makes them bounce more.

Tutorial 2.3 - requires Main Board, Switch Board, and LED Board.

Now for a more realistic example - this combines Tutorial 2.1 with Tutorial 1.9, the result is an LED sequencing program with four different patterns, selected by the four keys, with the key selected indicated by the corresponding LED.

;Tutorial 2.3 - Nigel Goodwin 2002
	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)

	cblock 	0x20 			;start of general purpose registers
		count			;used in table read routine
		count1 			;used in delay routine
		counta 			;used in delay routine 
		countb 			;used in delay routine
	endc

LEDPORT	Equ	PORTB			;set constant LEDPORT = 'PORTB'
LEDTRIS	Equ	TRISB			;set constant for TRIS register
SWPORT	Equ	PORTA
SWTRIS	Equ	TRISA
SW1	Equ	7			;set constants for the switches
SW2	Equ	6
SW3	Equ	5
SW4	Equ	4
LED1	Equ	3			;and for the LED's
LED2	Equ	2
LED3	Equ	1
LED4	Equ	0
	
	org	0x0000			;org sets the origin, 0x0000 for the 16F628,
					;this is where the program starts running	
	movlw	0x07
	movwf	CMCON			;turn comparators off (make it like a 16F84)

   	bsf 	STATUS,		RP0	;select bank 1
   	movlw 	b'00000000'		;set PortB all outputs
   	movwf 	LEDTRIS
	movlw 	b'11110000'		;set PortA 4 inputs, 4 outputs
   	movwf 	SWTRIS
	bcf	STATUS,		RP0	;select bank 0
	clrf	LEDPORT			;set all outputs low
	clrf	SWPORT
	bsf	SWPORT,	LED1		;set initial pattern


Start	clrf	count			;set counter register to zero
Read	movf	count, w		;put counter value in W
	btfsc	SWPORT,	LED1		;check which LED is lit
	call	Table1			;and read the associated table
	btfsc	SWPORT,	LED2
	call	Table2
	btfsc	SWPORT,	LED3
	call	Table3
	btfsc	SWPORT,	LED4
	call	Table4
	movwf	LEDPORT
	call	Delay
	incf	count,	w
	xorlw	d'14'			;check for last (14th) entry
	btfsc	STATUS,	Z
	goto	Start			;if start from beginning
	incf	count,	f		;else do next
	goto	Read

Table1	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'10000000'
        retlw   b'01000000'
        retlw   b'00100000'
        retlw   b'00010000'
        retlw   b'00001000'
        retlw   b'00000100'
        retlw   b'00000010'
        retlw   b'00000001'
        retlw   b'00000010'
        retlw   b'00000100'
        retlw   b'00001000'
        retlw   b'00010000'
        retlw   b'00100000'
        retlw   b'01000000'

Table2	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'11000000'
        retlw   b'01100000'
        retlw   b'00110000'
        retlw   b'00011000'
        retlw   b'00001100'
        retlw   b'00000110'
        retlw   b'00000011'
        retlw   b'00000011'
        retlw   b'00000110'
        retlw   b'00001100'
        retlw   b'00011000'
        retlw   b'00110000'
        retlw   b'01100000'
        retlw   b'11000000'

Table3	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'01111111'
        retlw   b'10111111'
        retlw   b'11011111'
        retlw   b'11101111'
        retlw   b'11110111'
        retlw   b'11111011'
        retlw   b'11111101'
        retlw   b'11111110'
        retlw   b'11111101'
        retlw   b'11111011'
        retlw   b'11110111'
        retlw   b'11101111'
        retlw   b'11011111'
        retlw   b'10111111'

Table4	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'00111111'
        retlw   b'10011111'
        retlw   b'11001111'
        retlw   b'11100111'
        retlw   b'11110011'
        retlw   b'11111001'
        retlw   b'11111100'
        retlw   b'11111100'
        retlw   b'11111001'
        retlw   b'11110011'
        retlw   b'11100111'
        retlw   b'11001111'
        retlw   b'10011111'
        retlw   b'00111111'

ChkKeys	btfss	SWPORT,	SW1
	call	Switch1
	btfss	SWPORT,	SW2
	call	Switch2
	btfss	SWPORT,	SW3
	call	Switch3
	btfss	SWPORT,	SW4
	call	Switch4
	retlw	0x00

Switch1	clrf	SWPORT			;turn all LED's off
	bsf	SWPORT,	LED1		;turn LED1 on
	retlw	0x00

Switch2	clrf	SWPORT			;turn all LED's off
	bsf	SWPORT,	LED2		;turn LED2 on
	retlw	0x00

Switch3	clrf	SWPORT			;turn all LED's off
	bsf	SWPORT,	LED3		;turn LED3 on
	retlw	0x00

Switch4	clrf	SWPORT			;turn all LED's off
	bsf	SWPORT,	LED4		;turn LED4 on
	retlw	0x00


Delay	movlw	d'250'			;delay 250 ms (4 MHz clock)
	movwf	count1
d1	call 	ChkKeys 		;check the keys
	movlw	0xC7			;delay 1mS
	movwf	counta
	movlw	0x01
	movwf	countb
Delay_0
	decfsz	counta, f
	goto	$+2
	decfsz	countb, f
	goto	Delay_0

	decfsz	count1	,f
	goto	d1
	retlw	0x00

	end      
      

The main differences here are in the Delay routine, which now has a call to check the keys every milli-second, and the main loop, where it selects one of four tables to read, depending on the settings of flag bits which are set according to which key was last pressed.

Tutorial 2.4 - requires Main Board, Switch Board, and LED Board.

Very similar to the last tutorial, except this one combines Tutorials 2.2 and 2.3 with Tutorial 1.9, the result is an LED sequencing program with three different patterns, selected by three of the keys, with the key selected indicated by the corresponding LED - the difference comes with the fourth switch, this selects slow or fast speeds, with the fast speed being indicated by a toggled LED.

;Tutorial 2.4 - Nigel Goodwin 2002
	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)

	cblock 	0x20 			;start of general purpose registers
		count			;used in table read routine
		count1 			;used in delay routine
		count2 			;used in delay routine
		counta 			;used in delay routine 
		countb
		countc
		countd
		speed
	endc

LEDPORT	Equ	PORTB			;set constant LEDPORT = 'PORTB'
LEDTRIS	Equ	TRISB			;set constant for TRIS register
SWPORT	Equ	PORTA
SWTRIS	Equ	TRISA
SW1	Equ	7			;set constants for the switches
SW2	Equ	6
SW3	Equ	5
SW4	Equ	4
LED1	Equ	3			;and for the LED's
LED2	Equ	2
LED3	Equ	1
LED4	Equ	0


SWDel	Set	Del50
	
	org	0x0000			;org sets the origin, 0x0000 for the 16F628,
					;this is where the program starts running	
	movlw	0x07
	movwf	CMCON			;turn comparators off (make it like a 16F84)

   	bsf 	STATUS,		RP0	;select bank 1
   	movlw 	b'00000000'		;set PortB all outputs
   	movwf 	LEDTRIS
	movlw 	b'11110000'		;set PortA 4 inputs, 4 outputs
   	movwf 	SWTRIS
	bcf	STATUS,		RP0	;select bank 0
	clrf	LEDPORT			;set all outputs low
	clrf	SWPORT			;make sure all LED's are off
	bsf	SWPORT,	LED1		;and turn initial LED on
	movlw	d'250'
	movwf	speed			;set initial speed



Start	clrf	count			;set counter register to zero
Read	movf	count, w		;put counter value in W
	btfsc	SWPORT,	LED1		;check which LED is on
	call	Table1			;and call the associated table
	btfsc	SWPORT,	LED2
	call	Table2
	btfsc	SWPORT,	LED3
	call	Table3
	movwf	LEDPORT
	call	DelVar
	incf	count,	w
	xorlw	d'14'			;check for last (14th) entry
	btfsc	STATUS,	Z
	goto	Start			;if start from beginning
	incf	count,	f		;else do next
	goto	Read

Table1	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'10000000'
        retlw   b'01000000'
        retlw   b'00100000'
        retlw   b'00010000'
        retlw   b'00001000'
        retlw   b'00000100'
        retlw   b'00000010'
        retlw   b'00000001'
        retlw   b'00000010'
        retlw   b'00000100'
        retlw   b'00001000'
        retlw   b'00010000'
        retlw   b'00100000'
        retlw   b'01000000'

Table2	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'11000000'
        retlw   b'01100000'
        retlw   b'00110000'
        retlw   b'00011000'
        retlw   b'00001100'
        retlw   b'00000110'
        retlw   b'00000011'
        retlw   b'00000011'
        retlw   b'00000110'
        retlw   b'00001100'
        retlw   b'00011000'
        retlw   b'00110000'
        retlw   b'01100000'
        retlw   b'11000000'

Table3	ADDWF   PCL, f			;data table for bit pattern
	retlw	b'01111111'
        retlw   b'10111111'
        retlw   b'11011111'
        retlw   b'11101111'
        retlw   b'11110111'
        retlw   b'11111011'
        retlw   b'11111101'
        retlw   b'11111110'
        retlw   b'11111101'
        retlw   b'11111011'
        retlw   b'11110111'
        retlw   b'11101111'
        retlw   b'11011111'
        retlw   b'10111111'

ChkKeys	btfss	SWPORT,	SW1
	call	Switch1
	btfss	SWPORT,	SW2
	call	Switch2
	btfss	SWPORT,	SW3
	call	Switch3
	btfss	SWPORT,	SW4
	call	Switch4
	retlw	0x00

Switch1	bcf	SWPORT,	LED2		;turn unselected LED's off
	bcf	SWPORT,	LED3		;turn unselected LED's off
	bsf	SWPORT,	LED1		;turn LED1 on
	retlw	0x00

Switch2	bcf	SWPORT,	LED1		;turn unselected LED's off
	bcf	SWPORT,	LED3		;turn unselected LED's off
	bsf	SWPORT,	LED2		;turn LED2 on
	retlw	0x00

Switch3	bcf	SWPORT,	LED1		;turn unselected LED's off
	bcf	SWPORT,	LED2		;turn unselected LED's off
	bsf	SWPORT,	LED3		;turn LED3 on
	retlw	0x00

Switch4	call	SWDel			;give switch time to stop bouncing
	btfsc	SWPORT,	SW4		;check it's still pressed
	retlw	0x00			;return is not
	btfss	SWPORT,	LED4		;see if LED4 is already lit
	goto	FASTON
	goto	FASTOFF

FASTON	bsf	SWPORT,	LED4		;turn LED4 on
	movlw	d'80'
	movwf	speed			;set fast speed
	call	SWDel
	btfsc	SWPORT,	SW4		;wait until button is released
	retlw	0x00
	goto	FASTON	

FASTOFF	bcf	SWPORT,	LED4		;turn LED4 on
	movlw	d'250'
	movwf	speed			;set slow speed
	call	SWDel
	btfsc	SWPORT,	SW4		;wait until button is released
	retlw	0x00
	goto	FASTOFF

DelVar	movfw	speed			;delay set by Speed
	movwf	count1
d1	call 	ChkKeys 		;check the keys
	movlw	0xC7			;delay 1mS
	movwf	counta
	movlw	0x01
	movwf	countb
Delay_0
	decfsz	counta, f
	goto	$+2
	decfsz	countb, f
	goto	Delay_0

	decfsz	count1	,f
	goto	d1
	retlw	0x00

;use separate delay routines, as Del50 is called from ChkKeys
;which is called from within DelVar

Del50	movlw	d'50'			;delay 50mS
 	movwf	count2
d3	movlw	0xC7			;delay 1mS
	movwf	countc
	movlw	0x01
	movwf	countd
Delay_1
	decfsz	countc, f
	goto	$+2
	decfsz	countd, f
	goto	Delay_1

	decfsz	count2	,f
	goto	d3
	retlw	0x00


	end